/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Sep 6, 2004
 *
 */
package jiggle.relo;

import jiggle.AbstractForceLaw;
import jiggle.Cell;
import jiggle.Edge;
import jiggle.Graph;

/**
 * This ForceLaw essentially models an attraction force in the target dimension
 * for nodes on the wrong side.
 *  
 * @author vineet
 *  
 */
public class PointedForceLaw extends AbstractForceLaw {

    AbstractForceLaw attrLaw;

    public PointedForceLaw(Graph g, double k, AbstractForceLaw attrLaw, AbstractForceLaw repulseLaw) {
        super(g, k);
        this.attrLaw = attrLaw;
    }

	@Override
    public double pairwiseWeights(double pref, double curr) {
		return attrLaw.pairwiseWeights(pref,curr);
	}
    
	@Override
    public void applyForCells(double[][] negativeGradient, Edge e) {
        if (!(e instanceof PointedEdge)) return;
        int tgtDim = ((PointedEdge) e).getDim();
        int tgtDimDir = ((PointedEdge) e).getIncrFactor();

        Cell from = e.getFrom();
		Cell to = e.getTo();
        if (from == to) return;

        double[] fromCoords = from.getCoords();
        double[] toCoords = to.getCoords();

        // only apply force if direction is invalid
        double prefToPos = fromCoords[tgtDim] 
				+ (from.getSize()[tgtDim]/2)
				+ (to.getSize()[tgtDim]/2)
				+ (preferredEdgeLength * tgtDimDir);


		//if (debugMode) {
		//    double[] tmpIdealToCoords = (double[]) toCoords.clone();
		//    tmpIdealToCoords[tgtDim] = prefToPos;
		//
		//    System.err.println(" pointed Err: " + (prefToPos - toCoords[tgtDim]) * pe.dimensionalDir);
		//    System.err.println(" dimensionalDir: " + pe.dimensionalDir);
		//    System.err.println(" preferredEdgeLength: " + preferredEdgeLength);
		//    System.err.println(" fromCoords: " + fromCoords[0] + "," + fromCoords[1]);
		//    System.err.println(" toCoords: " + toCoords[0] + "," + toCoords[1]);
		//    System.err.println(" idealToCoords: " + tmpIdealToCoords[0] + "," + tmpIdealToCoords[1]);
		//    System.err.println(" from: " + from);
		//    System.err.println(" to: " + to);
		//}

        // disable force when already on the correct side
        if ( (prefToPos - toCoords[tgtDim]) * tgtDimDir < 0) {
            return;
        }
        
        //double toMoveDist = prefToPos - toCoords[tgtDim];
        //double w = Math.min(toMoveDist / preferredEdgeLength, cap / toMoveDist);
        double[] idealToCoords = (double[]) toCoords.clone();
		idealToCoords[tgtDim] = prefToPos;

		double w = pairwiseWeights(to, idealToCoords);
        //double force = (toCoords[tgtDim] - fromCoords[tgtDim]) * w;
		double force = (toCoords[tgtDim] - idealToCoords[tgtDim]) * w;
        negativeGradient[to.gradientNdx][tgtDim] -= force * from.getWeight();
        negativeGradient[from.gradientNdx][tgtDim] += force * to.getWeight();
        //if (printDebugInfo) {
        //    System.err.println("PointedForceLaw: " + from + " --> " + to + " [" + tgtDim + "] Force: " + force);
        //}
        
    }

}